/*
 * logger.h
 *
 *  Created on: 2008-9-4
 *      Author: fanliangliang
 */

#ifndef LOGGER_H_
#define LOGGER_H_

#include "singleton.h"
#include "lock.h"
#include "timestamp.h"
#include <cstdarg>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>

// macros for convenience
#define xlog (*Logger::instance())

#define _LOGGER_(v) if (!Logger::instance()->isEnabledFor(v)) ; else Logger::instance()->setLogParameter(v, __FILE__, __LINE__)

#define xtrace _LOGGER_(LEVEL_TRACE)
#define xdebug _LOGGER_(LEVEL_DEBUG)
#define xinfo  _LOGGER_(LEVEL_INFO)
#define xwarn  _LOGGER_(LEVEL_WARN)
#define xerror _LOGGER_(LEVEL_ERROR)
#define xfatal _LOGGER_(LEVEL_FATAL)

BEGIN_NAMESPACE_TOXIC

using std::string;
using std::vector;
using std::ostringstream;
using std::ios_base;

// forward declaration
class LogAppender;
class Thread;

enum LogLevel {
	LEVEL_TRACE,
	LEVEL_DEBUG,
	LEVEL_INFO,
	LEVEL_WARN,
	LEVEL_ERROR,
	LEVEL_FATAL
};

struct LogEvent {
	Timestamp time;
	LogLevel level;
	unsigned tid;
	int line;
	string file;
	string message;
};

// make Logger class singleton
class Logger : public Singleton<Logger> {
	friend class Singleton<Logger>;
private:
	struct Sentry {
		Sentry() {}
		~Sentry() { Logger::instance()->endl(); }
	};

	// prevent clients from creating or destroying a Logger
private:
	Logger();
	~Logger();

	// public interface
public:
	void addAppender(ostream& stream);
	void addAppender(const char* filename, ios_base::openmode mode = ios_base::out | ios_base::trunc);
	void addAppender(LogAppender* appender);

	void setLevel(LogLevel level);
	LogLevel level() const;

	bool isEnabledFor(LogLevel level) const;
	void close();

	// workaround for the fucking cross-dll singleton design issues
	static Logger* instance();

	Logger& setLogParameter(LogLevel level, const char* file, int line, const Sentry& sentry = Sentry());
	void operator()(const char* format, ...);

	template <typename T>
	Logger& operator<<(const T& value) {
		tssMessage() << value;
		return *this;
	}

	// implemention details
private:
	void logFormat(const char* format, va_list alist);
	bool swapEvents(vector<LogEvent*>& events);
	LogEvent* createEvent(const string& message) const;
	void addEvent(LogEvent* e);
	void flush();
	void endl();
	void doLogger();

	// use tss(thread-specific storage) pattern to
	// ensure thread-safe and avoid synchronization
private:
	static LogEvent& tssEvent();
	static ostringstream& tssMessage();
	static bool& tssNeedEndl();

private:
	Mutex _appendersMutex;
	Mutex _eventsMutex;
	vector<LogAppender*> _appenders;
	vector<LogEvent*> _events;
	LogLevel _level;
	Thread* _logger;
	volatile bool _isAlive;
};

END_NAMESPACE_TOXIC

#endif /* LOGGER_H_ */
